package cn.newzai.parser.proto.result

import scala.collection.mutable.ArrayBuffer
import scala.collection.immutable.HashMap

/**
 * Created with IntelliJ IDEA.
 * User: chenxiaohong
 * Date: 13-6-4
 * Time: 上午9:37
 * To change this template use File | Settings | File Templates.
 */
object GenerateCodeType extends Enumeration{
  val
  CSharpCode ,
  CSharp2Protobuf,
  Protobuf2CSharp,
  CHarpFind ,
  XamlCode ,
  XamlCodeLiner,
  JavaCode,
  Java2Protobuf,
  Protobuf2Java,
  JavaFind,
  Wtkx,
  ScalaCode,
  Scala2Protobuf,
  ProtobufScala,
  ScalaFind ,
  CPlusplusCode,
  CPlusplus2Protobuf,
  Protobuf2CPlusplus,
  CPlusplusFind ,
  CSharpEnumValue2Index ,
  CSharpEnumValue2String,
  CSharpEnumResource,
  CSharpEnumCombox,
  CSharpEnumRadioGroup,
  CSharpClone = Value
}
abstract  class Field( val limit: Limit.Value,
             val data_type :Type,
             val name :String,
             val index : Int ,
             val default : String){
  override def toString: String = s"${limit} ${data_type} ${name} = ${index};"


  final def generate_code( gen_type : GenerateCodeType.Value) = data_type match {
     case BasicType(name) =>  generate_code_basic_type(gen_type,name)
     case EnumType(name)  =>  generate_code_enum_type(gen_type,name)
     case MessageType(name) =>generate_code_message_type(gen_type,name)
   }

  final def generate_code_basic_type( gen_type :GenerateCodeType.Value, typeName : String) = limit match {
    case Limit.required => generate_code_basic_type_required(gen_type,typeName)
    case Limit.optional => generate_code_basic_type_optional(gen_type,typeName)
    case Limit.repeated => generate_code_basic_type_repeated(gen_type,typeName)
  }
  final def generate_code_enum_type( gen_type :GenerateCodeType.Value, typeName : String) = limit match {
    case Limit.required => generate_code_enum_type_required(gen_type,typeName)
    case Limit.optional => generate_code_enum_type_optional(gen_type,typeName)
    case Limit.repeated => generate_code_enum_type_repeated(gen_type,typeName)
  }
  final def generate_code_message_type( gen_type :GenerateCodeType.Value, typeName : String) = limit match {
    case Limit.required => generate_code_message_type_required(gen_type,typeName)
    case Limit.optional => generate_code_message_type_optional(gen_type,typeName)
    case Limit.repeated => generate_code_message_type_repeated(gen_type,typeName)
  }

  def generate_code_basic_type_required( gen_type :GenerateCodeType.Value, typeName : String) :String//= s"/* ${gen_type} Nothing to do.*/"
  def generate_code_basic_type_optional( gen_type :GenerateCodeType.Value, typeName : String)  :String //= s"/* ${gen_type} Nothing to do.*/"
  def generate_code_basic_type_repeated( gen_type :GenerateCodeType.Value, typeName : String) :String //= s"/* ${gen_type} Nothing to do.*/"
  def generate_code_enum_type_required( gen_type :GenerateCodeType.Value, typeName : String)  :String//= s"/* ${gen_type} Nothing to do.*/"
  def generate_code_enum_type_optional( gen_type :GenerateCodeType.Value, typeName : String) :String //= s"/* ${gen_type} Nothing to do.*/"
  def generate_code_enum_type_repeated( gen_type :GenerateCodeType.Value, typeName : String)  :String//= s"/* ${gen_type} Nothing to do.*/"
  def generate_code_message_type_required( gen_type :GenerateCodeType.Value, typeName : String) :String //= s"/* ${gen_type} Nothing to do.*/"
  def generate_code_message_type_optional( gen_type :GenerateCodeType.Value, typeName : String) :String //= s"/* ${gen_type} Nothing to do.*/"
  def generate_code_message_type_repeated( gen_type :GenerateCodeType.Value, typeName : String)  :String//= s"/* ${gen_type} Nothing to do.*/"

   def toFieldName = s"_${name}"
   def toMethodName = {
     var firstOrUnderline = true
     val tmp = new StringBuilder()
     for( x <- name){
       if( firstOrUnderline){
         firstOrUnderline = false
         if( x == '_'){
           firstOrUnderline = true
         }else{
           tmp.append(x.toUpper)
         }
       } else{
          if( x == '_'  ){
            firstOrUnderline = true
          }else{
            tmp.append(x)
            if( x.isDigit){
              firstOrUnderline = true
            }
          }
       }

     }
     tmp.toString()
   }
}
object Field{
  def apply( limit:String, data_type :String, name :String, index :Int, default :String) ={
    import generate._

   // var l =  Limit.withName(limit)
   // var t =  ProtobufType.getType(data_type)

   // println(s"new Field : ${limit}/${l} ${data_type}/${t} ${name} = ${index}")
    new Field( Limit.withName(limit),ProtobufType.getType(data_type),name,index,default)

      with FieldGenerateCSharpCode
      with FieldGenerateCSharp2Protobuf
      with FieldGenerateProtobuf2CSharp
      with FieldGenerateCSharpClone

  }
}
trait FieldGenerate {
  this : Field =>
  override def generate_code_basic_type_required( gen_type :GenerateCodeType.Value, typeName : String) :String = s"/* ${gen_type} Nothing to do.*/"
  override def generate_code_basic_type_optional( gen_type :GenerateCodeType.Value, typeName : String) :String = s"/* ${gen_type} Nothing to do.*/"
  override def generate_code_basic_type_repeated( gen_type :GenerateCodeType.Value, typeName : String) :String  = s"/* ${gen_type} Nothing to do.*/"
  override def generate_code_enum_type_required( gen_type :GenerateCodeType.Value, typeName : String) :String = s"/* ${gen_type} Nothing to do.*/"
  override  def generate_code_enum_type_optional( gen_type :GenerateCodeType.Value, typeName : String) :String = s"/* ${gen_type} Nothing to do.*/"
  override  def generate_code_enum_type_repeated( gen_type :GenerateCodeType.Value, typeName : String)  :String = s"/* ${gen_type} Nothing to do.*/"
  override  def generate_code_message_type_required( gen_type :GenerateCodeType.Value, typeName : String):String  = s"/* ${gen_type} Nothing to do.*/"
  override  def generate_code_message_type_optional( gen_type :GenerateCodeType.Value, typeName : String) :String = s"/* ${gen_type} Nothing to do.*/"
  override  def generate_code_message_type_repeated( gen_type :GenerateCodeType.Value, typeName : String) :String = s"/* ${gen_type} Nothing to do.*/"

}
abstract  class Message(val name :String, val fields : Array[Field], val base : Field) {
  override def toString: String =
    s"""
      |message ${name}
      |{
      |   ${ fields.mkString("\n\t")}
      |}
    """.stripMargin

  def generate_code( gen_type : GenerateCodeType.Value) : String// = s"/* ${gen_type} Nothing to do..*/"
  lazy val liner_fields = LinerField(10)

  private def LinerField(depth : Int) : Array[(String,Field)]=
  {
      val liners = new ArrayBuffer[(String,Field)]()
    if( depth > 0)
    {
      for( f <- fields){
        f.data_type match {
          case MessageType(name) =>
            if( Message.hashMessage.contains(name))
            {
              val sub = Message.hashMessage(name)
              for( (path,subf) <- sub.LinerField(depth-1) ){
                //println(s"${name}: ${f.toMethodName}.${path}")
                liners += (( s"${f.toMethodName}.${path}", subf))
              }
            }
          case _ =>
           // println(s"${name}: ${f.toMethodName}")
            liners += ((f.toMethodName, f))
        }
      }
    }
    liners.toArray

  }

}

trait MessageGenerate {
  this : Message =>

  def generate_code( gen_type : GenerateCodeType.Value) = s"/* ${gen_type} Nothing to do..*/"
}
object Message{
  val message = new ArrayBuffer[Message]()
  private val hashMessage = scala.collection.mutable.HashMap[String,Message]()

  var package_name = ""

  def apply( name :String, fields : Array[Field]) ={
    import generate._

    val base = fields.find( _.name == "base")
    val fds = fields.filterNot( f => f.name == "base" || f.name.contains("include"))
    val m = new Message(name,fds,base.getOrElse(null))
      with MessageGenerateCSharpCode
      with MessageGenerateXamlCode
      with MessageGenerateXamlLinerCode

    message += m
    hashMessage += ((m.name,m))
    m
  }


}

object  ProtobufType extends Enumeration{
  val bool,int32,uint32,fixed32,sfixed32,int64,uint64,fixed64,sfixed64,string,bytes,enum,message = Value

  private val csharp_type_map_ = HashMap(bool->"bool",int32->"int",uint32->"uint",fixed32->"uint",sfixed32->"int",
  int64->"Int64",uint64->"UInt64",fixed64->"UInt64",sfixed64->"Int64",string->"string",bytes->"string")
  private val csharp_type_default_ = HashMap(bool->"false",int32->"0",uint32->"0",fixed32->"0",sfixed32->"0",
    int64->"0",uint64->"0",fixed64->"0",sfixed64->"0",string->"\"\"",bytes->"\"\"")

  def  csharp_type_map(key :Value) :String={
    if( csharp_type_map_.contains(key)){
      csharp_type_map_(key)
    }else{
      throw new NoSuchElementException(s"can't find type ${key} in csharp_type_map")
    }
  }

  def csharp_type_default( key : Value) : String ={
    if ( csharp_type_default_.contains(key)){
      csharp_type_default_(key)
    }else{
      throw new NoSuchElementException(s"can't find type ${key} in csharp_type_default")
    }
  }
  val scala_type_map = HashMap(bool->"Boolean",int32->"Int",uint32->"Int",fixed32->"Int",sfixed32->"Int",
    int64->"Int",uint64->"Int",fixed64->"Int",sfixed64->"Int",string->"String",bytes->"String")
  val scala_type_default = HashMap(bool->"false",int32->"0",uint32->"0",fixed32->"0",sfixed32->"0",
    int64->"0",uint64->"0",fixed64->"0",sfixed64->"0",string->"\"\"",bytes->"\"\"")
  def apply( name :String) ={
    name match {
      case "bool" => bool
      case "int32" => int32
      case "uint32" => uint32
      case "fixed32" => fixed32
      case "sfixed32" => sfixed32
      case "int64" => int64
      case "uint64" => uint64
      case "fixed64" => fixed64
      case "sfixed64" => sfixed64
      case "string" => string
      case "bytes" => bytes
      case  other => if( Enum.isEnum(other) ) enum else message
    }
  }
  def toCSharpType( name :String) ={
    val t = ProtobufType(name)
    t match{
      case `enum` => t
      case `message` => t
      case _ => csharp_type_map(t)
    }
  }
  def toScalaType( name :String) ={
    val t = ProtobufType(name)
    t match
    {
      case `enum` => t
      case `message` => t
      case _ => scala_type_map(t)
    }
  }
  def getType( name :String) ={
    apply(name) match {
      case `enum` => new EnumType(name)
      case `message` => new MessageType(name)
      case _ => new BasicType(name)
    }
  }
}
sealed  abstract class Type
case class BasicType(name :String ) extends Type
case class EnumType(name  :String) extends Type
case class MessageType(name :String) extends Type

object Limit extends Enumeration{
  val required,optional,repeated = Value
}